package oleg.match; 

/**
 * Copyright 2008,2009 Oleg Frenkel
 *
 * This file is part of PerfectSearch.
 *
 * PerfectSearch is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * PerfectSearch is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with PerfectSearch.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.util.*;
import javax.microedition.pim.Contact;

import oleg.*;
import oleg.log.Logger;
import oleg.cfg.Config;

/**
 * This class extends Matcher1 in the following way: when finding out what contacts
 * match input text, and the new input text starts with previous input text, then
 * we look through only those contacts that match previous input text, rather than 
 * going through all contacts.
 *
 * Description of Matcher1 (copied):
 * This class provides cache for finding out if specific contact matches input text
 * for prediction mode. This includes both "start with text" and "contains text".
 */

public class Matcher2 implements IMatcher
{
  private Matcher0 baseMatcher;

  // cache
  private Cache cache = Cache.instance();
  private static final String MATCH_START = "MATCH_START";
  private static final String MATCH_CONTAIN = "MATCH_CONTAIN";
  private static final String SEPARATOR = "_^%87_";

  private Vector allContacts;
  private Vector matchingStartNameContacts = new Vector();
  private Vector matchingContacts = new Vector();

  private Vector prevMatchingStartNameContacts = new Vector();
  private Vector prevMatchingContacts = new Vector();
  private String prevInputText = null;
  private int prevPadMode = -1;
  
  public Matcher2(Vector allContacts) {
    baseMatcher = new Matcher0(allContacts);
    setAllContacts(allContacts);
  }

  public void setAllContacts(Vector allContacts) {
    this.allContacts = allContacts;
    baseMatcher.setAllContacts(allContacts);
    clearCache();
    matchingStartNameContacts.removeAllElements();
    matchingContacts.removeAllElements();
    prevMatchingStartNameContacts = null;
    prevMatchingContacts = null;
    prevInputText = null;
    prevPadMode = -1;
  }

  public void clearCache() {
    cache.clear();
  }

  public void updateMatchingContacts(String inputText) {
    int padMode = Config.instance().getPadMode();
//     Logger.log("Matcher2: prevPadMode: " + prevPadMode +
//                " prevInputText: " + prevInputText +
//                " padMode: " + padMode +
//                " inputText: " + inputText);
    if (padMode == prevPadMode && inputText.startsWith(prevInputText)) {
      prevMatchingStartNameContacts = matchingStartNameContacts;
      prevMatchingContacts = matchingContacts;
    } else {
      prevMatchingStartNameContacts = allContacts;
      prevMatchingContacts = null;
    }

    prevPadMode = padMode;
    prevInputText = inputText;

    // Check cache
    matchingStartNameContacts = (Vector)cache.get(getCacheKey(MATCH_START, inputText, padMode));
    matchingContacts = (Vector)cache.get(getCacheKey(MATCH_CONTAIN, inputText, padMode));

    if (matchingStartNameContacts == null || matchingContacts == null) {
      matchingStartNameContacts = new Vector();
      matchingContacts = new Vector();

      for (int i = 0; i < prevMatchingStartNameContacts.size(); i++) {
        Contact curCont = (Contact)prevMatchingStartNameContacts.elementAt(i);

        switch (baseMatcher.matchContact(curCont, inputText)) {
        case C.START_MATCH:
          matchingStartNameContacts.addElement(curCont);
          break;
        case C.CONTAIN_MATCH:
          matchingContacts.addElement(curCont);
          break;
        }
      }

      if (prevMatchingContacts != null) {
        for (int i = 0; i < prevMatchingContacts.size(); i++) {
          Contact curCont = (Contact)prevMatchingContacts.elementAt(i);
          if (C.CONTAIN_MATCH == baseMatcher.matchContact(curCont, inputText)) {
            matchingContacts.addElement(curCont);
          }
        }
      }

//       Logger.log("1b");

      // update cache
      cache.put(getCacheKey(MATCH_START, inputText, padMode), matchingStartNameContacts);
      cache.put(getCacheKey(MATCH_CONTAIN, inputText, padMode), matchingContacts);
    }

//     Logger.log("Matcher2: 2");
  }

  public Vector getMatchingStartNameContacts() {
    return matchingStartNameContacts;
  }

  public Vector getMatchingContacts() {
    return matchingContacts;
  }

  public String getDisplayText(Contact curCont, String inputText) {
    return baseMatcher.getDisplayText(curCont, inputText);
  }

  // Cache functions
  private String getCacheKey(Vector values, String str) {
    StringBuffer sb = new StringBuffer(str + SEPARATOR);
    for (int i = 0; i < values.size(); i++) {
      sb.append((String)values.elementAt(i) + SEPARATOR);
    }

    return sb.toString();
  }

  private String getCacheKey(String matchType, String inputText, int padMode) {
    return matchType + SEPARATOR + inputText + SEPARATOR + padMode;
  }

}